package org.dandsoft.blackbox.snn.util.automaton;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: dvilyuzhanin
 * Date: 07.04.2009 18:24:48
 */
public class Automaton {

    public final static int MAX_PRIORITY = 0;
    public final static int MIN_PRIORITY = Integer.MAX_VALUE;

    private Collection<State> states = new HashSet<State>();
    private Map<State, LinkedList<ConnectionInfo>> connections = new Hashtable<State, LinkedList<ConnectionInfo>>();
    private State currentState;

    public synchronized void add(State state) {
        states.add(state);
        connections.put(state, new LinkedList<ConnectionInfo>());
    }

    /**
     * 
     * Вызывает connect(source, destination, connection, null)
     *
     * @param source
     * @param destination
     * @param connection
     * @throws AutomatonException
     */
    public synchronized void connect(State source, State destination, Connection connection) throws AutomatonException {
        connect(source, destination, connection, null);
    }

    /**
     * Объединяет новое состояние
     *
     * @param source
     * @param destination
     * @param connection
     * @param priority
     * @throws AutomatonException
     */
    public synchronized void connect(State source, State destination, Connection connection, Integer priority) throws AutomatonException {
        if (!states.contains(source)) {
            throw new AutomatonException("Source state not found: " + source);
        }

        if (!states.contains(destination)) {
            throw new AutomatonException("Destination state not found: " + destination);
        }

        ConnectionInfo connectionInfo = new ConnectionInfo();
        connectionInfo.connection = connection;
        connectionInfo.destination = destination;
        if (priority != null) {
            connectionInfo.priority = priority;
        } else {
            connectionInfo.priority = MIN_PRIORITY;
        }
        LinkedList<ConnectionInfo> collect = connections.get(source);
        if (priority != null && priority.intValue() > -1) {
            ListIterator<ConnectionInfo> iterator = collect.listIterator();
            while (iterator.hasNext()) {
                ConnectionInfo info = iterator.next();
                if (priority < info.priority) {
                    iterator.previous();
                    iterator.add(connectionInfo);
                    break;
                }
            }
        } else {
            collect.add(connectionInfo);
        }

    }

    public State getCurrentState() {
        return currentState;
    }

    public void setCurrentState(State currentState) {
        if (!states.contains(currentState)) {
            throw new IllegalArgumentException("Undefined current state: " + currentState);
        }
        this.currentState = currentState;
        this.currentState.in();
    }

    /**
     * Выполняет переход к следующему состоянию
     *
     * @return true - переход выполнен. false - переход не выполнен, тупик
     * @throws AutomatonException Если текущее состояние неопределено или переход не возможен ни оподному из переходов.
     */
    public synchronized boolean nextState() throws AutomatonException {
        if (currentState == null) {
            //текущее состояние неопределено
            throw new AutomatonException("Unknown current state");
        }
        //получаем все переходы из текущей вершины
        Collection<ConnectionInfo> collect = connections.get(currentState);
        if (collect.size() == 0) {
            //переходов нет. Тупик
            return false;
        } else {
            boolean isOk = false;
            //опрашиваем все переходы
            for (ConnectionInfo info : collect) {
                if (info.connection.isGoOut(currentState)) {
                    //перехохим по первому кто ответил true
                    if (info.destination == currentState) {
                        //целевая вершина тажа что и исходная. Тогда это петля
                        currentState.loop();
                    } else {
                        //покаидаем текущую вергину
                        currentState.out();
                        currentState = info.destination;
                        //входим в новую вершину
                        currentState.in();
                    }
                    isOk = true;
                    break;
                }
            }

            if (!isOk) {
                //переход не возможен ни по одной из вершин
                throw new AutomatonException("Dead lock: " + currentState);
            }
            return true;
        }
    }


    class ConnectionInfo {
        public Connection connection;
        public State destination;
        public int priority;
    }
}
